/*
    This file is part of LWX. 

    LWX is the abbreviation of the Light WiMax simulation program which 
    is a ns2 WiMAX network simulation module. LWX allows each user to 
    apply his/her designed scheduling algorithm or other mechanism used 
    in WiMAX network and generate the corresponding simulation results
    for further analysis or other non-commercial purposes.

    Copyright (C) 2008 Yen-Hung Cheng (pplong), NTUST in Taiwan, R.O.C.
    E-Mail: lwx.ns2@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "random.h"
#include "ll.h"
#include "mac.h"
#include "timer-handler.h"
#include "rtp.h" // for udp packet

#include <iostream>
#include <sstream>
#include <string>
#include <queue>
#include <vector>

using namespace std;

// frame duration definition
#define FRAME_PERIOD              0.0049
#define RTG_PERIOD                0.0001
#define PACKETS_PER_SECOND        200
#define FRAME_SYMBOL_NUM          48
#define DL_SYMBOL_NUM             36
#define UL_SYMBOL_NUM             12

#define DL_ACCESS_ZONE_SYMBOL     24
#define DL_RELAY_ZONE_SYMBOL      12
#define UL_ACCESS_ZONE_SYMBOL     6
#define UL_RELAY_ZONE_SYMBOL      6


#define DL_PUSC_SUBCH_NUM         60
#define UL_PUSC_SUBCH_NUM         70
#define DL_PUSC_SLOT_SYMBOL_NUM   2
#define UL_PUSC_SLOT_SYMBOL_NUM   3



// flow type definition
#define FLOW_TYPE_BS_TO_SS                  1
#define FLOW_TYPE_SS_TO_BS                  2
#define FLOW_TYPE_BS_TO_RS                  3
#define FLOW_TYPE_RS_TO_BS                  4
#define FLOW_TYPE_SS_TO_RS_TP               5
#define FLOW_TYPE_SS_TO_RS_NONTP            6
#define FLOW_TYPE_RS_TO_SS_TP               7
#define FLOW_TYPE_RS_TO_SS_NONTP            8

// node type definition
#define NODE_TYPE_BS    1
#define NODE_TYPE_SS    2
#define NODE_TYPE_RS    3

// bandwidth allocation type 
// 0 : default test
// 1 : round robin
// 2 : strict priority

static int BWA_Algorithm_Type;

class LWX;
class Flow_Queue;
class LWX_Node;
class LWX_Node_Info;
class LWX_Relay_Link_Info;

//////////////////////////////////
// timer handler
//////////////////////////////////

class LWXTimer : public Handler
{
  public:
    LWXTimer(LWX* m):mac(m),rtime(0){busy_ = paused_ = 0; stime = 0.0;};
    virtual void	handle(Event *e)=0;		
    virtual void	start(double time);
    virtual void	stop(void);
    virtual void	pause(void) {assert(0);}	//to be overloaded in subclass
    virtual void	resume(void) {assert(0);}	//to be overloaded in subclass
    inline int	busy(void) {return busy_;}
    inline int	paused(void) {return paused_;}
    
    
  protected:
    LWX	*mac;
    int			busy_;
    int			paused_;
    Event		event;
    Event		intr;		
    double		stime;		//start time
    double		wtime;		//waiting time
    double    rtime;
};

class Frame_LWXTimer:public LWXTimer{
  public:
    Frame_LWXTimer(LWX* m):LWXTimer(m){};
    int 	bcid_;
    int     pkt_count_;
    double  interval_;
    void 	start(double time);	
    void 	stop();	
    void	handle(Event *e);
};


class RTG_LWXTimer:public LWXTimer{
  public:
    RTG_LWXTimer(LWX* m):LWXTimer(m){};
    int 	bcid_;
    int     pkt_count_;
    double  interval_;
    void 	start(double time);	
    void 	stop();	
    void	handle(Event *e);
};

//////////////////////////////////
// LWX Mac
//////////////////////////////////

class LWX : public Mac {
	
  public:
    LWX();
	  void recv(Packet *p, Handler *h);
	  void send_down(Packet *p, Handler *h);
    void send_up(Packet *p, Handler *h);
	  int  command(int, const char*const*);

    // for new timer handler start
    Frame_LWXTimer *frame_timer ;
    void handle_flow_trans(int node_id);
   
    RTG_LWXTimer *rtg_timer ;
    void handle_rtg(int node_id);
    // for new timer handler end
    
    // bandwidth allocation start
    void bandwidth_allocation(int node_id);
    void BWA_Test(int node_id);
    void BWA_Round_Robin(int node_id);
    void BWA_Strict_Priority(int node_id);
    void BWA_Relay_Sample(int node_id);
    // bandwidth allocation end
	
	  // for finding function start
    bool is_bs(int node_id);
    int find_mac_addr(int node_id);
	  // for finding function end
    
    // for showing msg start
    void lwx_err_msg(string where, string note=" ");
    void lwx_test_msg(string where, string note=" ");
    // for showing msg end
   
    
    // for packet transmission start
    void trans_ss_to_bs(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_bs_to_ss(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_bs_to_rs(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_rs_to_bs(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_ss_to_rs_tp(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_ss_to_rs_nontp(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_rs_to_ss_tp(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    void trans_rs_to_ss_nontp(int node_id, vector <Flow_Queue*> &tmp_flow_list);
    // for packet transmission end
    
	private:
	  bool send_Ctrl_Pkt(void);
};

//////////////////////////////////
// LWX packet header (mac layer)
//////////////////////////////////

struct hdr_lwx
{
  int src_ip_;
  int src_port_;
  int src_mac_;
  int dst_ip_;
  int dst_port_;
  int dst_mac_;
  int src_node_id_;
  int from_node_id_;
  int to_node_id_;
  int bwa_flow_type_;
  double sendtime_;

  static int offset_;
  inline static int& offset() { return offset_; }
  inline static hdr_lwx* access(const Packet* p) {
    return (hdr_lwx*) p->access(offset_);
  }

  int& src_ip() { return (src_ip_);}
  int& src_port() { return (src_port_);}
  int& src_mac() { return (src_mac_);}
  int& dst_ip() { return (dst_ip_);}
  int& dst_port() { return (dst_port_);}
  int& dst_mac() { return (dst_mac_);}
  int& src_node_id() { return (src_node_id_);}
  int& from_node_id() { return (from_node_id_);}
  int& to_node_id() { return (to_node_id_);}
  int& bwa_flow_type() { return (bwa_flow_type_);}
  double& sendtime() { return (sendtime_);}

};


//////////////////////////////////
// LWX Tcl Class
//////////////////////////////////

static class LWXClass : public TclClass 
{
  public:
 
    // for tcl binding c++ static var and invoke c++ function start
    void lwx_err_msg(int argc,const char* const* argv, string other_msg=" ");
    vector <string> lwx_str_split(string str, string delim);
    virtual void bind();
    virtual int method(int argc,const char* const* argv);
    // for tcl binding c++ static var and invoke c++ function end
	
	  LWXClass() : TclClass("Mac/LWX") {}
	
	  TclObject* create(int, const char*const*) 
	  {
		  return new LWX();
    }
	
} class_maclwx;

//////////////////////////////////
// LWX packet queue class
//////////////////////////////////

class Flow_Queue
{
  public:
    int src_node_id;
    int from_node_id;
    int to_node_id;
    
    int src_ip;
    int src_port;
    int dst_ip;
    int dst_port;
    
    // flow_type
    int flow_type;
    
    int QoS_Class; // ugs:1, ert-vr:2, rt-vr:3, nrt-vr:4, be:5 (default)
    int Rmin; // bps
    int Rmax; // bps
    int Lmax; // ms
    int Jitter; // ms
    
    double rate; // default rate (access link) byte
    
    unsigned long int pkt_sum_size; // bytes 
    
    vector <int> BWA;               // bytes
    int    BWA_Total;
    int    BWA_Total_Byte;
    int    BWA_Backoff;             // wicked packetlization idea
    
    vector <LWX_Relay_Link_Info> relay_link;  
    int    BWA_Flow_Type;           // for RS or mesh
    int    BWA_Next_Hop;            // for RS or mesh
    double BWA_Rate;                // for RS or mesh
    
    queue <Packet*> lwx_pkt;
   
    
    
    Flow_Queue()
    {
      src_node_id = -1;
      from_node_id = -1;
      to_node_id = -1;
      
      src_ip = -1;
      src_port = -1;
      dst_ip = -1;
      dst_port =-1;
      
      flow_type=-1;
      QoS_Class=5;
      Rmin=0;
      Rmax=0;
      Lmax=20;  
      Jitter=20;
      
      rate=0;
      
      BWA_Total= -1;
      BWA_Total_Byte= -1;
      BWA_Backoff = -1;
      BWA_Flow_Type = -1;
      pkt_sum_size=0;
      BWA_Next_Hop = -1;
    }
   
    unsigned long int queue_size()
    {
      return this-> pkt_sum_size;
    } 
   
    int queue_length()
    {
      return this->lwx_pkt.size();
    }

    void pkt_pop()
    {
      Packet *tmp_pkt=NULL;
      
      if(!this->lwx_pkt.empty())
      {
        tmp_pkt=this->lwx_pkt.front();
        this->lwx_pkt.pop();
        pkt_sum_size-=hdr_cmn::access(tmp_pkt)->size();
      }
    }
    
    Packet* pkt_front()
    {
      Packet *tmp_pkt=NULL;
      
      if(!this->lwx_pkt.empty())
      {
        tmp_pkt=this->lwx_pkt.front();
      }
      
      return tmp_pkt;
    }
    
    void pkt_push(Packet *tmp_pkt)
    {
      
      pkt_sum_size+=hdr_cmn::access(tmp_pkt)->size();
      this->lwx_pkt.push(tmp_pkt);
      
      if(this->queue_size() > 4000000000)
      {
        cout << "packet queue full!! "
             << " node_id = " <<  src_node_id
             << " src_ip = " <<  src_ip
             << " src_port = " << src_port
             << " dst_ip = " <<  dst_ip 
             << " dst_port = " << dst_port
             << " flow_type = " << flow_type
             << endl;
        exit(1); // force simulation to terminate
      }
    }
};

class LWX_Node_Info
{
  public:
    int node_id;
    int node_type;
};

class LWX_Node
{
  public:
    int node_mac_addr;
    int node_id;
    int node_type;              
    
    vector <LWX_Node_Info> ctrl_node;
    vector <Flow_Queue>    ctrl_flow;
    
    LWX_Node()
    {
      node_mac_addr=-1;
      node_id=-1;
      node_type=-1;
    }
};

class LWX_Relay_Link_Info
{
  public:
    
    int rs_node_id;
    double rate;
    int flow_type;
    
};

static vector <LWX_Node> lwx_node_list;
